// 数据响应式
/**
 * 在key发生变化时可以感知做出操作
 * @param {*} obj 对象
 * @param {*} key 需要拦截的key
 * @param {*} val 初始值
 */
 function defineReactive(obj, key, val) {
  //  递归
  observer(val);
  // 创建Dep实例
  // data中的数据每一项都会进入到此，创建一个Dep
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    get() {
      console.log('get', key);
      // 依赖收集
      // 只有在调用时存在target才会被Dep收集更新（在初始化时设置静态属性target为watcher，被收集）
      Dep.target && dep.addDep(Dep.target)
      return val
    },
    set(newVal) {
      console.log('set', newVal);
      if (newVal != val) {
        observer(newVal)
        val = newVal
        // watchers.forEach(w => w.update())
        // 被修改时通知所有属于自己的watcher更新
        // 一个watcher对应一处依赖，一个Dep对应一个data中的数据  一个dep的更新可以指向多个watcher
        dep.notify()
      }
    }
  })
}
// 遍历obj做响应式
function observer(obj) {
  if(typeof obj !== 'object' || obj === null) {
    return
  }
  // 遍历obj的所有key做响应式
  new Observer(obj);
}
// 遍历obj的所有key做响应式
class Observer {
  constructor(value) {
    this.value = value
    if(Array.isArray(this.value)) {
      // TODO
    } else {
      this.walk(value)
    }
  }
  // 对象响应式
  walk(obj) {
    Object.keys(obj).forEach(key => {
      defineReactive(obj, key, obj[key])
    })
  }
}
// function set(obj, key, val) {
//   defineReactive(obj, key, val)
// }

function proxy(vm) {
  Object.keys(vm.$data).forEach(key => {
    Object.defineProperty(vm, key, {
      get: () => vm.$data[key],
      set: (v) => vm.$data[key] = v
    })
  })
}

class Vue{
  constructor(options) {
    this.$options = options;
    this.$data = options.data
    // this.$methods = options.methods
    // observer(this.$data)
    proxy(this)
    // new Compile(options.el, this)
  }
}
// 解析模板
// 1.处理插值
// 2.处理指令和事件
// 3.以上两者的初始化和更新
class Compile {
  constructor(el, vm) {
    this.$vm = vm
    this.$el = document.querySelector(el)

    if(this.$el) {
      this.compile(this.$el)
    }
  }
  compile(el) {
    // 遍历el的子节点 判断他们的类型做相应的处理
    const childNodes = el.childNodes
    if(!childNodes) return;
    childNodes.forEach(node => {
      if(node.nodeType === 1) {
        // 元素 处理指令和事件
        // console.log('元素', node.nodeName);
        const attrs = node.attributes
        // console.log('attrs', attrs);
        Array.from(attrs).forEach(attr => {
          // c-xxx="abc"
          const attrName = attr.name
          const exp = attr.value
          // console.log('attrName', attrName, exp);
          // console.log(attr.startsWith('c-'));
          if(attrName.startsWith('c-')) {
            const dir = attrName.substring(2)
            this[dir] && this[dir](node, exp)
          } else if(attrName.startsWith('@')) {
            const dir = attrName.substring(1)
            // this[dir] && this[dir](node, exp)
            this.eventFun(node, exp, dir)
          }
        })
      } else if(this.isInter(node)) {
        // 文本
        // console.log('插值', node.textContent);
        this.compileText(node)
      }
      // 递归
      if(node.childNodes) {
        this.compile(node)
      }
    })
  }
  // 点击事件
  click(node, exp) {
    // console.log(11, node, exp, this.$vm.$methods[exp]);
    node.addEventListener("click", this.$vm.$methods[exp].bind(this.$vm))
  }
  // 事件处理函数
  eventFun(node, exp, dir) {
    node.addEventListener(dir, this.$vm.$methods[exp].bind(this.$vm))
  }

  update(node, exp, dir) {
    // 初始化
    const fn = this[dir + 'Update']
    fn && fn(node, this.$vm[exp])
    // 更新
    new Watcher(this.$vm, exp, function(val) {
      fn && fn(node, val)
    })
  }
  
  text(node, exp) {
    // node.textContent = this.$vm[exp]
    this.update(node, exp, 'text')
  }
  textUpdate(node, val) {
    node.textContent = val
  }
  modelUpdate(node, val) {
    node.value = val
  }
  html(node, exp) {
    // node.innerHTML = this.$vm[exp]
    this.update(node, exp, 'html')
  }
  htmlUpdate(node, val) {
    // this.update(node, this.$vm[RegExp.$1], 'html')
    node.innerHTML = val
  }

  // 编译文本
  compileText(node) {
    // node.textContent = this.$vm[RegExp.$1]
    this.update(node, RegExp.$1, 'text')
  }

  // 双向绑定处理
  model(node, exp) {
    // console.log(node, exp);
    this.update(node, exp, 'model')
    node.addEventListener('input', (e) => {
      // console.log(e.target.value);
      // console.log(this);
      this.$vm[exp] = e.target.value
    })
  }

  // 是否插值表达式
  isInter(node) {
    // console.log('node', node);
    return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent)
  }

}

// 监听器：负责依赖更新
// const watchers = [];
class Watcher {
  constructor(vm, key, updateFn) {
    this.vm = vm;
    this.key = key;
    this.updateFn = updateFn
    // watchers.push(this)

    // 触发依赖收集
    Dep.target = this
    this.vm[this.key]
    Dep.target = null
  }

  // 未来被Dep调用
  update() {
    // 执行实际的更新操作
    this.updateFn.call(this.vm, this.vm[this.key])
  }
}

class Dep{
  constructor() {
    this.deps = []
  }
  addDep(dep) {
    this.deps.push(dep)
  }
  notify() {
    this.deps.forEach(dep => dep.update())
  }
}
